// 《围棋》作者版权所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package reflectdata

import (
	"fmt"
	"math/bits"
	"sort"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/objw"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/internal/obj"
)

// isRegularMemory报告t是否可以作为常规内存进行比较/散列。
func isRegularMemory(t *types.Type) bool {
	a, _ := types.AlgType(t)
	return a == types.AMEM
}

// eqCanPanic报告类型t上的==是否会出现恐慌（在某处有一个接口）。
// t必须具有可比性。
func eqCanPanic(t *types.Type) bool {
	switch t.Kind() {
	default:
		return false
	case types.TINTER:
		return true
	case types.TARRAY:
		return eqCanPanic(t.Elem())
	case types.TSTRUCT:
		for _, f := range t.FieldSlice() {
			if !f.Sym.IsBlank() && eqCanPanic(f.Type) {
				return true
			}
		}
		return false
	}
}

// AlgType尽可能返回固定宽度的AMEMxx变体，而不是一般的
// AMEM种类。
func AlgType(t *types.Type) types.AlgKind {
	a, _ := types.AlgType(t)
	if a == types.AMEM {
		if t.Alignment() < int64(base.Ctxt.Arch.Alignment) && t.Alignment() < t.Size() {
			// 例如，如果int32需要
			// 4字节对齐，我们不能将[2]int16视为int32。见第46283期。
			return a
		}
		switch t.Size() {
		case 0:
			return types.AMEM0
		case 1:
			return types.AMEM8
		case 2:
			return types.AMEM16
		case 4:
			return types.AMEM32
		case 8:
			return types.AMEM64
		case 16:
			return types.AMEM128
		}
	}

	return a
}

// genhash返回一个符号，该符号是用于计算
// t类型值的哈希值的闭包。
// 注意：生成的函数必须与运行时匹配。完全正确。
func genhash(t *types.Type) *obj.LSym {
	switch AlgType(t) {
	default:
		// genhash只对具有相等性的类型调用
		base.Fatalf("genhash %v", t)
	case types.AMEM0:
		return sysClosure("memhash0")
	case types.AMEM8:
		return sysClosure("memhash8")
	case types.AMEM16:
		return sysClosure("memhash16")
	case types.AMEM32:
		return sysClosure("memhash32")
	case types.AMEM64:
		return sysClosure("memhash64")
	case types.AMEM128:
		return sysClosure("memhash128")
	case types.ASTRING:
		return sysClosure("strhash")
	case types.AINTER:
		return sysClosure("interhash")
	case types.ANILINTER:
		return sysClosure("nilinterhash")
	case types.AFLOAT32:
		return sysClosure("f32hash")
	case types.AFLOAT64:
		return sysClosure("f64hash")
	case types.ACPLX64:
		return sysClosure("c64hash")
	case types.ACPLX128:
		return sysClosure("c128hash")
	case types.AMEM:
		// 对于其他大小的普通内存，我们构建了一个闭包
		// 该闭包调用memhash_varlen。内存大小为
		// 编码在闭包的第一个插槽中。
		closure := TypeLinksymLookup(fmt.Sprintf(".hashfunc%d", t.Size()))
		if len(closure.P) > 0 { // 已生成
			return closure
		}
		if memhashvarlen == nil {
			memhashvarlen = typecheck.LookupRuntimeFunc("memhash_varlen")
		}
		ot := 0
		ot = objw.SymPtr(closure, ot, memhashvarlen, 0)
		ot = objw.Uintptr(closure, ot, uint64(t.Size())) // 闭包中编码的大小
		objw.Global(closure, int32(ot), obj.DUPOK|obj.RODATA)
		return closure
	case types.ASPECIAL:
		break
	}

	closure := TypeLinksymPrefix(".hashfunc", t)
	if len(closure.P) > 0 { // 已生成
		return closure
	}

	// 为子类型生成哈希函数。
	// 在某些情况下，我们可能不使用这些哈希，但在这种情况下，它们将被消除死代码。
	// （由genhash生成的闭包也将消除
	// 死代码，我们直接称之为子类型哈希器
	// ）
	switch t.Kind() {
	case types.TARRAY:
		genhash(t.Elem())
	case types.TSTRUCT:
		for _, f := range t.FieldSlice() {
			genhash(f.Type)
		}
	}

	sym := TypeSymPrefix(".hash", t)
	if base.Flag.LowerR != 0 {
		fmt.Printf("genhash %v %v %v\n", closure, sym, t)
	}

	base.Pos = base.AutogeneratedPos // 比输入端
	typecheck.DeclContext = ir.PEXTERN

	// func sym（p*T，h uintptpr）uintptpr 
	args := []*ir.Field{
		ir.NewField(base.Pos, typecheck.Lookup("p"), nil, types.NewPtr(t)),
		ir.NewField(base.Pos, typecheck.Lookup("h"), nil, types.Types[types.TUINTPTR]),
	}
	results := []*ir.Field{ir.NewField(base.Pos, nil, nil, types.Types[types.TUINTPTR])}
	tfn := ir.NewFuncType(base.Pos, nil, args, results)

	fn := typecheck.DeclFunc(sym, tfn)
	np := ir.AsNode(tfn.Type().Params().Field(0).Nname)
	nh := ir.AsNode(tfn.Type().Params().Field(1).Nname)

	switch t.Kind() {
	case types.TARRAY:
		// 纯内存数组将由
		// 标准算法处理，因此元素类型不能是
		// 纯内存。
		hashel := hashfor(t.Elem())

		// 对于i:=0；我是内勒姆；i++
		ni := typecheck.Temp(types.Types[types.TINT])
		init := ir.NewAssignStmt(base.Pos, ni, ir.NewInt(0))
		cond := ir.NewBinaryExpr(base.Pos, ir.OLT, ni, ir.NewInt(t.NumElem()))
		post := ir.NewAssignStmt(base.Pos, ni, ir.NewBinaryExpr(base.Pos, ir.OADD, ni, ir.NewInt(1)))
		loop := ir.NewForStmt(base.Pos, nil, cond, post, nil)
		loop.PtrInit().Append(init)

		// h=hashel（&p[i]，h）
		call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil)

		nx := ir.NewIndexExpr(base.Pos, np, ni)
		nx.SetBounded(true)
		na := typecheck.NodAddr(nx)
		call.Args.Append(na)
		call.Args.Append(nh)
		loop.Body.Append(ir.NewAssignStmt(base.Pos, nh, call))

		fn.Body.Append(loop)

	case types.TSTRUCT:
		// 在运行AMEM 
		// 时使用memhash遍历结构，并为其他函数调用特定的哈希函数。
		for i, fields := 0, t.FieldSlice(); i < len(fields); {
			f := fields[i]

			// 跳过空白字段。
			if f.Sym.IsBlank() {
				i++
				continue
			}

			// 使用适当的哈希函数对非内存字段进行哈希。
			if !isRegularMemory(f.Type) {
				hashel := hashfor(f.Type)
				call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil)
				nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym) // TODO:来自其他包的字段？
				na := typecheck.NodAddr(nx)
				call.Args.Append(na)
				call.Args.Append(nh)
				fn.Body.Append(ir.NewAssignStmt(base.Pos, nh, call))
				i++
				continue
			}

			// 否则，散列原始内存的最大运行长度。
			size, next := memrun(t, i)

			// h=hashel（&p.first，size，h）
			hashel := hashmem(f.Type)
			call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil)
			nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym) // TODO:来自其他包的字段？
			na := typecheck.NodAddr(nx)
			call.Args.Append(na)
			call.Args.Append(nh)
			call.Args.Append(ir.NewInt(size))
			fn.Body.Append(ir.NewAssignStmt(base.Pos, nh, call))

			i = next
		}
	}

	r := ir.NewReturnStmt(base.Pos, nil)
	r.Results.Append(nh)
	fn.Body.Append(r)

	if base.Flag.LowerR != 0 {
		ir.DumpList("genhash body", fn.Body)
	}

	typecheck.FinishFuncBody()

	fn.SetDupok(true)
	typecheck.Func(fn)

	ir.CurFunc = fn
	typecheck.Stmts(fn.Body)
	ir.CurFunc = nil

	if base.Debug.DclStack != 0 {
		types.CheckDclstack()
	}

	fn.SetNilCheckDisabled(true)
	typecheck.Target.Decls = append(typecheck.Target.Decls, fn)

	// 构建结束。它不覆盖任何变量，因此
	// 它只包含函数指针。
	objw.SymPtr(closure, 0, fn.Linksym(), 0)
	objw.Global(closure, int32(types.PtrSize), obj.DUPOK|obj.RODATA)

	return closure
}

func hashfor(t *types.Type) ir.Node {
	var sym *types.Sym

	switch a, _ := types.AlgType(t); a {
	case types.AMEM:
		base.Fatalf("hashfor with AMEM type")
	case types.AINTER:
		sym = ir.Pkgs.Runtime.Lookup("interhash")
	case types.ANILINTER:
		sym = ir.Pkgs.Runtime.Lookup("nilinterhash")
	case types.ASTRING:
		sym = ir.Pkgs.Runtime.Lookup("strhash")
	case types.AFLOAT32:
		sym = ir.Pkgs.Runtime.Lookup("f32hash")
	case types.AFLOAT64:
		sym = ir.Pkgs.Runtime.Lookup("f64hash")
	case types.ACPLX64:
		sym = ir.Pkgs.Runtime.Lookup("c64hash")
	case types.ACPLX128:
		sym = ir.Pkgs.Runtime.Lookup("c128hash")
	default:
		// 注意：hashfor的调用者通过调用t的genhash来确保这个符号
		// 存在并有一个主体。
		sym = TypeSymPrefix(".hash", t)
	}

	// TODO（austin）：这会创建一个ir。使用nil Func命名。
	n := typecheck.NewName(sym)
	ir.MarkFunc(n)
	n.SetType(types.NewSignature(types.NoPkg, nil, nil, []*types.Field{
		types.NewField(base.Pos, nil, types.NewPtr(t)),
		types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
	}, []*types.Field{
		types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
	}))
	return n
}

// sysClosure返回一个闭包，该闭包将调用
// 给定的运行时函数（没有闭合变量）。
func sysClosure(name string) *obj.LSym {
	s := typecheck.LookupRuntimeVar(name + "·f")
	if len(s.P) == 0 {
		f := typecheck.LookupRuntimeFunc(name)
		objw.SymPtr(s, 0, f, 0)
		objw.Global(s, int32(types.PtrSize), obj.DUPOK|obj.RODATA)
	}
	return s
}

// geneq返回一个符号，该符号是用于计算两个类型为t的对象的
// 相等的闭包。
func geneq(t *types.Type) *obj.LSym {
	switch AlgType(t) {
	case types.ANOEQ:
		// 如果运行时试图将
		// 一个类型与一个等于零的函数进行比较，则运行时会死机。
		return nil
	case types.AMEM0:
		return sysClosure("memequal0")
	case types.AMEM8:
		return sysClosure("memequal8")
	case types.AMEM16:
		return sysClosure("memequal16")
	case types.AMEM32:
		return sysClosure("memequal32")
	case types.AMEM64:
		return sysClosure("memequal64")
	case types.AMEM128:
		return sysClosure("memequal128")
	case types.ASTRING:
		return sysClosure("strequal")
	case types.AINTER:
		return sysClosure("interequal")
	case types.ANILINTER:
		return sysClosure("nilinterequal")
	case types.AFLOAT32:
		return sysClosure("f32equal")
	case types.AFLOAT64:
		return sysClosure("f64equal")
	case types.ACPLX64:
		return sysClosure("c64equal")
	case types.ACPLX128:
		return sysClosure("c128equal")
	case types.AMEM:
		// 使平等结束。
		// 类型的大小编码在闭包中。
		closure := TypeLinksymLookup(fmt.Sprintf(".eqfunc%d", t.Size()))
		if len(closure.P) != 0 {
			return closure
		}
		if memequalvarlen == nil {
			memequalvarlen = typecheck.LookupRuntimeFunc("memequal_varlen")
		}
		ot := 0
		ot = objw.SymPtr(closure, ot, memequalvarlen, 0)
		ot = objw.Uintptr(closure, ot, uint64(t.Size()))
		objw.Global(closure, int32(ot), obj.DUPOK|obj.RODATA)
		return closure
	case types.ASPECIAL:
		break
	}

	closure := TypeLinksymPrefix(".eqfunc", t)
	if len(closure.P) > 0 { // 已生成
		return closure
	}
	sym := TypeSymPrefix(".eq", t)
	if base.Flag.LowerR != 0 {
		fmt.Printf("geneq %v\n", t)
	}

	// 自动生成结构和数组相等的代码。

	base.Pos = base.AutogeneratedPos // 比输入端
	typecheck.DeclContext = ir.PEXTERN

	// func sym（p，q*T）bool 
	tfn := ir.NewFuncType(base.Pos, nil,
		[]*ir.Field{ir.NewField(base.Pos, typecheck.Lookup("p"), nil, types.NewPtr(t)), ir.NewField(base.Pos, typecheck.Lookup("q"), nil, types.NewPtr(t))},
		[]*ir.Field{ir.NewField(base.Pos, typecheck.Lookup("r"), nil, types.Types[types.TBOOL])})

	fn := typecheck.DeclFunc(sym, tfn)
	np := ir.AsNode(tfn.Type().Params().Field(0).Nname)
	nq := ir.AsNode(tfn.Type().Params().Field(1).Nname)
	nr := ir.AsNode(tfn.Type().Results().Field(0).Nname)

	// 如果相等性测试失败，则跳转到的标签更少混乱。
	neq := typecheck.AutoLabel(".neq")

	// 我们这里只针对具有相等性的类型，但是
	// 不能由标准算法处理，所以t必须是数组或结构。
	switch t.Kind() {
	default:
		base.Fatalf("geneq %v", t)

	case types.TARRAY:
		nelem := t.NumElem()

		// checkAll生成代码来检查所有数组元素的相等性。
		// 如果展开大于nelem，则checkAll生成：
		// 
		// 如果等式（p[0]，q[0]）&等式（p[1]，q[1]）&。。。{
		// }其他{
		// 返回
		// }
		// 
		// 等等。
		// 
		// 否则它将生成：
		// 
		// for i:=0；我是内勒姆；q（p[i]，q
		checkAll := func(unroll int64, last bool, eq func(pi, qi ir.Node) ir.Node) {
			// checkIdx生成一个节点来检查索引i处的相等性。
			checkIdx := func(i ir.Node) ir.Node {
				// pi:=p[i]
				pi := ir.NewIndexExpr(base.Pos, np, i)
				pi.SetBounded(true)
				pi.SetType(t.Elem())
				// qi:=q[i]
				qi := ir.NewIndexExpr(base.Pos, nq, i)
				qi.SetBounded(true)
				qi.SetType(t.Elem())
				return eq(pi, qi)
			}

			if nelem <= unroll {
				if last {
					// 以不同的方式进行最后的比较。
					nelem--
				}
				// 生成一系列检查。
				for i := int64(0); i < nelem; i++ {
					// 如果检查{}其他{goto neq}
					nif := ir.NewIfStmt(base.Pos, checkIdx(ir.NewInt(i)), nil, nil)
					nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
					fn.Body.Append(nif)
				}
				if last {
					fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, checkIdx(ir.NewInt(nelem))))
				}
			} else {
				// 生成for循环。
				// 对于i:=0；我是内勒姆；i++
				i := typecheck.Temp(types.Types[types.TINT])
				init := ir.NewAssignStmt(base.Pos, i, ir.NewInt(0))
				cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(nelem))
				post := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1)))
				loop := ir.NewForStmt(base.Pos, nil, cond, post, nil)
				loop.PtrInit().Append(init)
				// 如果等式（pi，qi）{}其他{goto neq}
				nif := ir.NewIfStmt(base.Pos, checkIdx(i), nil, nil)
				nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
				loop.Body.Append(nif)
				fn.Body.Append(loop)
				if last {
					fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true)))
				}
			}
		}

		switch t.Elem().Kind() {
		case types.TSTRING:
			// 做两个循环。首先，检查所有长度是否匹配（便宜）。
			// 其次，检查所有内容是否匹配（昂贵）。
			// TODO:当数组大小较小时，展开长度匹配检查。
			checkAll(3, false, func(pi, qi ir.Node) ir.Node {
				// 比较长度。
				eqlen, _ := EqString(pi, qi)
				return eqlen
			})
			checkAll(1, true, func(pi, qi ir.Node) ir.Node {
				// 比较内容。
				_, eqmem := EqString(pi, qi)
				return eqmem
			})
		case types.TFLOAT32, types.TFLOAT64:
			checkAll(2, true, func(pi, qi ir.Node) ir.Node {
				// p[i]==q[i]
				return ir.NewBinaryExpr(base.Pos, ir.OEQ, pi, qi)
			})
		// TODO:拆开结构，也把它们零碎地做起来
		default:
			checkAll(1, true, func(pi, qi ir.Node) ir.Node {
				// p[i]==q[i]
				return ir.NewBinaryExpr(base.Pos, ir.OEQ, pi, qi)
			})
		}

	case types.TSTRUCT:
		// 建立一个要满足的条件列表。
		// 条件是一个列表列表。条件在每个内部列表中都是可重排序的。外部列表必须按顺序进行评估。
		var conds [][]ir.Node
		conds = append(conds, []ir.Node{})
		and := func(n ir.Node) {
			i := len(conds) - 1
			conds[i] = append(conds[i], n)
		}

		// 运行AMEM 
		// 时使用memequal遍历结构，并调用其他的特定相等测试。
		for i, fields := 0, t.FieldSlice(); i < len(fields); {
			f := fields[i]

			// 跳过空白的命名字段。
			if f.Sym.IsBlank() {
				i++
				continue
			}

			// 将非内存字段与字段相等进行比较。
			if !isRegularMemory(f.Type) {
				if eqCanPanic(f.Type) {
					// 通过启动一组新的可重定额条件来执行排序。
					conds = append(conds, []ir.Node{})
				}
				p := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym)
				q := ir.NewSelectorExpr(base.Pos, ir.OXDOT, nq, f.Sym)
				switch {
				case f.Type.IsString():
					eqlen, eqmem := EqString(p, q)
					and(eqlen)
					and(eqmem)
				default:
					and(ir.NewBinaryExpr(base.Pos, ir.OEQ, p, q))
				}
				if eqCanPanic(f.Type) {
					// 在可能引起恐慌的事情发生后，也要执行命令。
					conds = append(conds, []ir.Node{})
				}
				i++
				continue
			}

			// 查找仅内存字段的最大运行长度。
			size, next := memrun(t, i)

			// TODO（rsc）：对于
			// 跨包未报告字段，所有对newname的调用都是错误的。
			if s := fields[i:next]; len(s) <= 2 {
				// 两个或更少的字段：使用普通字段相等。
				for _, f := range s {
					and(eqfield(np, nq, f.Sym))
				}
			} else {
				// 两个以上字段：使用memequal。
				and(eqmem(np, nq, f.Sym, size))
			}
			i = next
		}

		// 对条件进行排序，以将运行时调用放在最后。
		// 保留订单的其余部分。
		var flatConds []ir.Node
		for _, c := range conds {
			isCall := func(n ir.Node) bool {
				return n.Op() == ir.OCALL || n.Op() == ir.OCALLFUNC
			}
			sort.SliceStable(c, func(i, j int) bool {
				return !isCall(c[i]) && isCall(c[j])
			})
			flatConds = append(flatConds, c...)
		}

		if len(flatConds) == 0 {
			fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true)))
		} else {
			for _, c := range flatConds[:len(flatConds)-1] {
				// if cond{else{goto neq}
				n := ir.NewIfStmt(base.Pos, c, nil, nil)
				n.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
				fn.Body.Append(n)
			}
			fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, flatConds[len(flatConds)-1]))
		}
	}

	// ret:
	// return 
	ret := typecheck.AutoLabel(".ret")
	fn.Body.Append(ir.NewLabelStmt(base.Pos, ret))
	fn.Body.Append(ir.NewReturnStmt(base.Pos, nil))

	// neq:
	// r=false 
	// return 
	fn.Body.Append(ir.NewLabelStmt(base.Pos, neq))
	fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(false)))
	if eqCanPanic(t) || anyCall(fn) {
		// 尾声很大，请与其他人分享。
		fn.Body.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, ret))
	} else {
		// 结束语很小，所以不要麻烦分享。
		fn.Body.Append(ir.NewReturnStmt(base.Pos, nil))
	}
	// TODO（khr）：上述尾声大小检测条件并不完美。
	// 我们真的应该做一个通用的CL，在董事会中分享尾声。见#24936。

	if base.Flag.LowerR != 0 {
		ir.DumpList("geneq body", fn.Body)
	}

	typecheck.FinishFuncBody()

	fn.SetDupok(true)
	typecheck.Func(fn)

	ir.CurFunc = fn
	typecheck.Stmts(fn.Body)
	ir.CurFunc = nil

	if base.Debug.DclStack != 0 {
		types.CheckDclstack()
	}

	// 编译此代码时禁用checknils。
	// 我们正在比较一个结构或一个数组，
	// 两者都不能为零，我们的比较
	// 很肤浅。
	fn.SetNilCheckDisabled(true)
	typecheck.Target.Decls = append(typecheck.Target.Decls, fn)

	// 生成一个闭包，该闭包指向我们刚刚生成的函数。
	objw.SymPtr(closure, 0, fn.Linksym(), 0)
	objw.Global(closure, int32(types.PtrSize), obj.DUPOK|obj.RODATA)
	return closure
}

func anyCall(fn *ir.Func) bool {
	return ir.Any(fn, func(n ir.Node) bool {
		// TODO（rsc）：没有方法？
		op := n.Op()
		return op == ir.OCALL || op == ir.OCALLFUNC
	})
}

// eqfield返回节点
// p.field==q.field 
func eqfield(p ir.Node, q ir.Node, field *types.Sym) ir.Node {
	nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field)
	ny := ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field)
	ne := ir.NewBinaryExpr(base.Pos, ir.OEQ, nx, ny)
	return ne
}

// EqString返回节点
// len（s）==len len（t）
// 和
// memequal s（s.ptr，t.ptr，len 
// 可用于构造字符串相等比较。
// eqlen必须在eqmem之前进行评估，并且需要短路。
func EqString(s, t ir.Node) (eqlen *ir.BinaryExpr, eqmem *ir.CallExpr) {
	s = typecheck.Conv(s, types.Types[types.TSTRING])
	t = typecheck.Conv(t, types.Types[types.TSTRING])
	sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s)
	tptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, t)
	slen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, s), types.Types[types.TUINTPTR])
	tlen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, t), types.Types[types.TUINTPTR])

	fn := typecheck.LookupRuntime("memequal")
	fn = typecheck.SubstArgTypes(fn, types.Types[types.TUINT8], types.Types[types.TUINT8])
	call := typecheck.Call(base.Pos, fn, []ir.Node{sptr, tptr, ir.Copy(slen)}, false).(*ir.CallExpr)

	cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, slen, tlen)
	cmp = typecheck.Expr(cmp).(*ir.BinaryExpr)
	cmp.SetType(types.Types[types.TBOOL])
	return cmp, call
}

// EqInterface返回节点
// s.tab==t.tab（或s.typ==t.typ，视情况而定）
// 和
// ifaceeq（s.tab，s.data，t.data）（或efaceeq（s.typ，s.data，t.data），视情况而定）
// eqdata之前必须评估eqtab，并且需要短路。
func EqInterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) {
	if !types.Identical(s.Type(), t.Type()) {
		base.Fatalf("EqInterface %v %v", s.Type(), t.Type())
	}
	// func-ifaceeq（tab*uintpttr，x，y unsafe.Pointer）（ret bool）
	// func-efaceeq（typ*uintptpr，x，y unsafe.Pointer）（ret bool）
	var fn ir.Node
	if s.Type().IsEmptyInterface() {
		fn = typecheck.LookupRuntime("efaceeq")
	} else {
		fn = typecheck.LookupRuntime("ifaceeq")
	}

	stab := ir.NewUnaryExpr(base.Pos, ir.OITAB, s)
	ttab := ir.NewUnaryExpr(base.Pos, ir.OITAB, t)
	sdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, s)
	tdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, t)
	sdata.SetType(types.Types[types.TUNSAFEPTR])
	tdata.SetType(types.Types[types.TUNSAFEPTR])
	sdata.SetTypecheck(1)
	tdata.SetTypecheck(1)

	call := typecheck.Call(base.Pos, fn, []ir.Node{stab, sdata, tdata}, false).(*ir.CallExpr)

	cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, stab, ttab)
	cmp = typecheck.Expr(cmp).(*ir.BinaryExpr)
	cmp.SetType(types.Types[types.TBOOL])
	return cmp, call
}

// eqmemem返回节点
func eqmem(p ir.Node, q ir.Node, field *types.Sym, size int64) ir.Node {
	nx := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field)))
	ny := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field)))

	fn, needsize := eqmemfunc(size, nx.Type().Elem())
	call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
	call.Args.Append(nx)
	call.Args.Append(ny)
	if needsize {
		call.Args.Append(ir.NewInt(size))
	}

	return call
}

func eqmemfunc(size int64, t *types.Type) (fn *ir.Name, needsize bool) {
	switch size {
	default:
		fn = typecheck.LookupRuntime("memequal")
		needsize = true
	case 1, 2, 4, 8, 16:
		buf := fmt.Sprintf("memequal%d", int(size)*8)
		fn = typecheck.LookupRuntime(buf)
	}

	fn = typecheck.SubstArgTypes(fn, t, t)
	return fn, needsize
}

// t是父结构类型，start是启动运行的字段索引。
// size是运行中包含的内存的字节长度。
// 接下来是内存运行结束后的索引。
func memrun(t *types.Type, start int) (size int64, next int) {
	next = start
	for {
		next++
		if next == t.NumFields() {
			break
		}
		// 在一个填充的场地后停止跑步。
		if types.IsPaddedField(t, next-1) {
			break
		}
		// 同样，在空白或非内存字段前停止。
		if f := t.Field(next); f.Sym.IsBlank() || !isRegularMemory(f.Type) {
			break
		}
		// 对于问题46283，如果生成的加载将
		// 需要比组件字段更大的对齐，请不要合并字段。
		if base.Ctxt.Arch.Alignment > 1 {
			align := t.Alignment()
			if off := t.Field(start).Offset; off&(align-1) != 0 {
				// 偏移量的对齐程度低于包含的类型。
				// 使用偏移来确定对齐。
				align = 1 << uint(bits.TrailingZeros64(uint64(off)))
			}
			size := t.Field(next).End() - t.Field(start).Offset
			if size > align {
				break
			}
		}
	}
	return t.Field(next-1).End() - t.Field(start).Offset, next
}

func hashmem(t *types.Type) ir.Node {
	sym := ir.Pkgs.Runtime.Lookup("memhash")

	// TODO（奥斯汀）：这会产生ir。使用nil Func命名。
	n := typecheck.NewName(sym)
	ir.MarkFunc(n)
	n.SetType(types.NewSignature(types.NoPkg, nil, nil, []*types.Field{
		types.NewField(base.Pos, nil, types.NewPtr(t)),
		types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
		types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
	}, []*types.Field{
		types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
	}))
	return n
}
